package org.dromara.common.mybatis.helper

import cn.dev33.satoken.context.SaHolder
import cn.hutool.core.collection.CollectionUtil
import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy
import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper
import org.dromara.common.core.utils.reflect.ReflectUtils.getField
import org.dromara.common.core.utils.reflect.ReflectUtils.getStaticFieldValue
import org.dromara.common.mybatis.annotation.DataPermission
import java.util.*
import java.util.function.Supplier

/**
 * 数据权限助手
 *
 * @author Lion Li
 * @version 3.5.0
 * @updater LikeYouDo
 * @date 2025/1/27 3:38
 */
@Suppress("UNCHECKED_CAST")
object DataPermissionHelper {
    private const val DATA_PERMISSION_KEY: String = "data:permission"

    private val REENTRANT_IGNORE: ThreadLocal<Stack<Int>> = ThreadLocal.withInitial { Stack() }

    private val PERMISSION_CACHE: ThreadLocal<DataPermission> = ThreadLocal()

    /**
     * 获取当前执行mapper权限注解
     *
     * @return 返回当前执行mapper权限注解
     */
    @JvmStatic
    fun getPermission(): DataPermission? {
        return PERMISSION_CACHE.get()
    }

    /**
     * 设置当前执行mapper权限注解
     *
     * @param dataPermission   数据权限注解
     */
    @JvmStatic
    fun setPermission(dataPermission: DataPermission?) {
        PERMISSION_CACHE.set(dataPermission)
    }

    /**
     * 删除当前执行mapper权限注解
     */
    @JvmStatic
    fun removePermission() {
        PERMISSION_CACHE.remove()
    }

    /**
     * 从上下文中获取指定键的变量值，并将其转换为指定的类型
     *
     * @param key 变量的键
     * @param <T> 变量值的类型
     * @return 指定键的变量值，如果不存在则返回 null
     */
    @JvmStatic
    fun <T> getVariable(key: String): T? {
        val context: Map<String, Any> = getContext()
        return context[key] as T?
    }

    /**
     * 向上下文中设置指定键的变量值
     *
     * @param key   要设置的变量的键
     * @param value 要设置的变量值
     */
    @JvmStatic
    fun setVariable(key: String, value: Any?) {
        val context = getContext()
        context[key] = value as Any
    }

    /**
     * 获取数据权限上下文
     *
     * @return 存储在SaStorage中的Map对象，用于存储数据权限相关的上下文信息
     * @throws NullPointerException 如果数据权限上下文类型异常，则抛出NullPointerException
     */
    @JvmStatic
    fun getContext(): MutableMap<String, Any> {
        val saStorage = SaHolder.getStorage()
        var attribute = saStorage[DATA_PERMISSION_KEY]
        if (attribute === null) {
            saStorage[DATA_PERMISSION_KEY] = HashMap<Any, Any>()
            attribute = saStorage[DATA_PERMISSION_KEY]
        }
        if (attribute is Map<*, *>) {
            return attribute as MutableMap<String, Any>
        }
        throw NullPointerException("data permission context type exception")
    }

    private fun getIgnoreStrategy(): IgnoreStrategy? {
        val ignoreStrategyLocal = getStaticFieldValue(
            getField(
                InterceptorIgnoreHelper::class.java, "IGNORE_STRATEGY_LOCAL"
            )
        )
        if (ignoreStrategyLocal is ThreadLocal<*>) {
            if (ignoreStrategyLocal.get() is IgnoreStrategy) {
                return ignoreStrategyLocal.get() as IgnoreStrategy
            }
        }
        return null
    }

    /**
     * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭)
     */
    @JvmStatic
    fun enableIgnore() {
        val ignoreStrategy = getIgnoreStrategy()
        if (ignoreStrategy === null) {
            InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build())
        } else {
            ignoreStrategy.dataPermission = true
        }
        val reentrantStack = REENTRANT_IGNORE.get()
        reentrantStack.push(reentrantStack.size + 1)
    }

    /**
     * 关闭忽略数据权限
     */
    @JvmStatic
    fun disableIgnore() {
        val ignoreStrategy = getIgnoreStrategy()
        if (ignoreStrategy != null) {
            val noOtherIgnoreStrategy = true != ignoreStrategy.dynamicTableName
                && true != ignoreStrategy.blockAttack
                && true != ignoreStrategy.illegalSql
                && true != ignoreStrategy.tenantLine
                && CollectionUtil.isEmpty(ignoreStrategy.others)
            val reentrantStack = REENTRANT_IGNORE.get()
            val empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1
            if (noOtherIgnoreStrategy && empty) {
                InterceptorIgnoreHelper.clearIgnoreStrategy()
            } else if (empty) {
                ignoreStrategy.dataPermission = false
            }
        }
    }

    /**
     * 在忽略数据权限中执行
     *
     * @param handle 处理执行方法
     */
    @JvmStatic
    fun ignore(handle: Runnable) {
        enableIgnore()
        try {
            handle.run()
        } finally {
            disableIgnore()
        }
    }

    /**
     * 在忽略数据权限中执行
     *
     * @param handle 处理执行方法
     */
    @JvmStatic
    fun <T> ignore(handle: Supplier<T>): T {
        enableIgnore()
        try {
            return handle.get()
        } finally {
            disableIgnore()
        }
    }
}
